package bma.common.langutil.ai.vm;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

import bma.common.langutil.concurrent.TimerManager;

/**
 * 
 * @author guanzhong
 * @since 1.0 (imported from BMA.ESP Project)
 * 
 */
public class AIVM {

	protected static AIVM main = new AIVM();

	public void installMain() {
		main = this;
	}

	protected static ThreadLocal<AIThread> local = new ThreadLocal<AIThread>();

	protected ThreadGroup threadGroup;
	protected String name = "aivmt";
	protected long stackSize = 0;
	protected int threadSize;
	protected TimerManager timer;

	protected AIThreadImpl[] threads;

	public static AIVM main() {
		return main;
	}

	public static AIVM current() {
		AIThread t = local.get();
		return t == null ? null : t.getVM();
	}

	public static AIThread currentThread() {
		return local.get();
	}

	public AIVM() {
		super();
		threadSize = Runtime.getRuntime().availableProcessors();
	}

	public ThreadGroup getThreadGroup() {
		return threadGroup;
	}

	public void setThreadGroup(ThreadGroup threadGroup) {
		this.threadGroup = threadGroup;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public long getStackSize() {
		return stackSize;
	}

	public void setStackSize(long stackSize) {
		this.stackSize = stackSize;
	}

	public int getThreadSize() {
		return threadSize;
	}

	public void setThreadSize(int threadSize) {
		this.threadSize = threadSize;
	}

	public TimerManager getTimer() {
		return timer;
	}

	public void setTimer(TimerManager timer) {
		this.timer = timer;
	}

	public TimerManager sureTimer() {
		if (timer == null) {
			TimerManager tm = TimerManager.getInstance();
			if (tm != null)
				return tm;
			synchronized (this) {
				timer = new TimerManager();
				timer.setExecutor(Executors.newSingleThreadExecutor());
				timer.startManager();
			}
		}
		return timer;
	}

	public void initMain() throws Exception {
		init();
		installMain();
	}

	public void init() throws Exception {
		List<Future<Boolean>> slist = new LinkedList<Future<Boolean>>();
		threads = new AIThreadImpl[threadSize];
		for (int i = 0; i < threadSize; i++) {
			threads[i] = new AIThreadImpl(this, i + 1);
			slist.add(threads[i].core.startProcess());
		}
		Iterator<Future<Boolean>> iter = slist.iterator();
		while (iter.hasNext()) {
			Future<Boolean> f = iter.next();
			if (!f.get()) {
				throw new IllegalStateException("AIVM(" + name + ") start fail");
			}
		}
	}

	public void close() {
		if (this.threads != null) {
			List<Future<Boolean>> slist = new LinkedList<Future<Boolean>>();
			for (int i = 0; i < threadSize; i++) {
				if (threads[i] != null) {
					slist.add(threads[i].core.close());
				}
			}
			Iterator<Future<Boolean>> iter = slist.iterator();
			while (iter.hasNext()) {
				Future<Boolean> f = iter.next();
				try {
					f.get(30, TimeUnit.SECONDS);
				} catch (Exception e) {
				}
			}
		}
		if (this.timer != null) {
			this.timer.stopManager();
		}
	}

	public static enum Picker {
		LOAD, HASH
	}

	public static interface Pickable {
		public Picker getPicker();
	}

	public boolean execute(Object router, Runnable command) {
		int id = -1;
		if (router != null) {
			if (router instanceof AIThreadId) {
				AIThreadId tid = (AIThreadId) router;
				id = tid.get();
			} else if (router instanceof Pickable) {
				Picker p = ((Pickable) router).getPicker();
				if (p != null) {
					if (Picker.HASH.equals(p)) {
						id = hashPicker(router.hashCode());
					}
				}
			}
		}
		if (id == -1) {
			id = loadPicker();
		}
		if (id > threadSize) {
			id = hashPicker(id);
		}
		threads[id].execute(command);
		return false;
	}

	public int hashPicker(int hash) {
		return hash % threadSize;
	}

	public int loadPicker() {
		int s = 0;
		int maxl = Integer.MAX_VALUE;
		for (int i = 0; i < threadSize; i++) {
			int l = threads[i].load();
			if (l < maxl) {
				s = i;
				maxl = l;
			}
		}
		return s;
	}

	public AIContext createContext(Object router) {
		return new AIContextVM(this, router);
	}

	public TimerManager checkTimer(TimerManager timer) {
		if (timer != null)
			return timer;
		return TimerManager.checkInstance(this.timer);
	}

	public static TimerManager checkMainTimer(TimerManager timer) {
		if (timer != null)
			return timer;
		return main.sureTimer();
	}

	public static TimerManager timer() {
		if (main == null)
			return null;
		return main.sureTimer();
	}
}
